package main

import(
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"github.com/spf13/pflag"
)

type selpg_args struct{
	startPage      int  //开始页
	endPage        int //结束页
	input         string //输入文件
	pageLen        int //页长（页行数）
	pageType       bool  //两种类型，true表示-f，false表示-lnumber
	output string // 输出文件
} 

//检查输入命令是否合法
func check(sa *selpg_args){
	if sa==nil{
		fmt.Fprintf(os.Stderr,"\n[Error]The args is nil! Please check your program!\n\n")
		os.Exit(0)
	}else if (sa.startPage==-1)||(sa.endPage==-1){
		fmt.Fprintf(os.Stderr,"\n[Error]The startPage and endPage do not allowed empty! Please check your command.\n\n")
		os.Exit(0)
	}else if sa.startPage>sa.endPage{
		fmt.Fprintf(os.Stderr,"\n[Error]The startPage can not be bigger than the endPage! Please check your command!\n\n")
		os.Exit(0)
	}else if (sa.startPage<1)||(sa.endPage<1){
		fmt.Fprintf(os.Stderr,"\n[Error]The startPage and endPage do not exist! Please check your command.\n\n")
		os.Exit(0)
	}
	if sa.pageType==false&&sa.pageLen<1 {
		fmt.Fprintf(os.Stderr,"\n[Error]You should input valid page length!\n\n")
		os.Exit(0)
	}
}

func ArgsInit(sa *selpg_args){
	pflag.IntVarP(&(sa.startPage),"startPage","s",-1,"start page")
	pflag.IntVarP(&(sa.endPage),"endPage","e",-1,"end page")
	pflag.IntVarP(&(sa.pageLen),"pageLen","l",5,"the length of page")
	pflag.BoolVarP(&(sa.pageType),"pageType","f",false,"page type")
	pflag.StringVarP(&(sa.output),"output","d","","print destination")
	pflag.Parse()
	sa_left:=pflag.Args() // 其余参数
	if len(sa_left) > 0 {
		sa.input=sa_left[0]
	} else {
		sa.input=""
	}
	check(sa)
}

func Input(sa *selpg_args){
	var reader *bufio.Reader
	var cmd *exec.Cmd=nil
	var stdin io.WriteCloser=nil
	writer:=bufio.NewWriter(os.Stdout)
	if sa.input=="" {
		reader=bufio.NewReader(os.Stdin)
	} else{
		fileIn,err:=os.Open(sa.input)
		defer fileIn.Close()
		if err!=nil {
			os.Stderr.Write([]byte("Open file error.\n"))
			os.Exit(0)
		}
		reader=bufio.NewReader(fileIn)
	}
	
	if sa.output!="" {
		cmd=exec.Command(sa.output)
		var pipeErr error;
		stdin,pipeErr=cmd.StdinPipe()
		if pipeErr!=nil {
			fmt.Println(pipeErr)
			os.Exit(0)
		}
		startErr:=cmd.Start()
		if startErr!=nil {
			fmt.Println(startErr)
			os.Exit(0)
		}
	}
	lineCtr:=0
	pageCtr:=1
	endSign:='\n'
	if sa.pageType==true {
		endSign='\f'
	}
	for{
		strLine,errRead:=reader.ReadBytes(byte(endSign))
		if errRead!=nil {
			if errRead==io.EOF {
				writer.Flush()
				break
			} else{
				os.Stderr.Write([]byte("Read bytes from reader failed.\n"))
				os.Exit(0)
			}
		}
		if pageCtr>=sa.startPage&&pageCtr<=sa.endPage {
			_,errWrite:=writer.Write(strLine)
			if errWrite!=nil {
				fmt.Println(errWrite)
				os.Stderr.Write([]byte("Write bytes to out failed.\n"))
				os.Exit(0)
			}
			if stdin!=nil {
				_,errWrite:=stdin.Write(strLine)
				if errWrite!=nil {
					fmt.Println(errWrite)
					os.Stderr.Write([]byte("Write bytes to out failed.\n"))
					os.Exit(0)
				}
			}
		}
		if sa.pageType==true {
			pageCtr++
		} else{
			lineCtr++
		}
		if sa.pageType==false&&lineCtr==sa.pageLen {
			lineCtr=0
			pageCtr++
		}
		if pageCtr>sa.endPage {
			writer.Flush()
			break
		}
	}
	if stdin!=nil {
		stdin.Close()
	}
	if cmd!=nil {
		if err:=cmd.Wait();err!=nil {
			fmt.Println(err)
			os.Exit(0)
		}
	}
}

func main(){
	var sa selpg_args
	ArgsInit(&sa)
	Input(&sa)
}
